package com.slowfly.rule.engine.service.impl;


import com.slowfly.rule.engine.component.DingTalkComponent;
import com.slowfly.rule.engine.component.RedisComponent;
import com.slowfly.rule.engine.constants.IRiskConstants;
import com.slowfly.rule.engine.entity.StrategyRecord;
import com.slowfly.rule.engine.enums.ResultTypeEnum;
import com.slowfly.rule.engine.enums.StrategyStatusEnum;
import com.slowfly.rule.engine.model.Fuse;
import com.slowfly.rule.engine.param.StrategyParam;
import com.slowfly.rule.engine.service.IStrategyFuseService;
import com.slowfly.rule.engine.service.IStrategyService;
import com.slowfly.rule.engine.util.JsonUtils;
import com.slowfly.rule.engine.util.NumberUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Objects;
import java.util.concurrent.TimeUnit;


/**
 * 熔断服务实现类
 */
@Slf4j
@Service
public class StrategyFuseServiceImpl implements IStrategyFuseService {

    @Autowired
    private IStrategyService strategyService;
    @Autowired
    private RedisComponent redisComponent;
    @Autowired
    private DingTalkComponent dingTalkComponent;

    /**
     * 熔断策略
     */
    public void riskStrategyFuse(Fuse fuse, StrategyRecord strategyRecord) {
        // 策略必须是上线状态
        if (!StrategyStatusEnum.isOnline(strategyRecord.getStrategyStatus())) {
            return;
        }
        // 策略必须正确配置了熔断
        if (Objects.isNull(fuse)
                || Objects.isNull(fuse.getTime()) || fuse.getTime() < NumberUtil.ZERO
                || Objects.isNull(fuse.getCount()) || fuse.getCount() < NumberUtil.ZERO
                || Objects.isNull(fuse.getRatio()) || fuse.getRatio() < NumberUtil.ZERO || fuse.getRatio() > NumberUtil.ONE) {
            return;
        }
        try {
            strategyFuse(fuse, strategyRecord);
        } catch (Exception e) {
            log.error("fuseStrategy error {} {}", strategyRecord.getRiskId(), JsonUtils.toJson(strategyRecord), e);
        }
    }

    private void strategyFuse(Fuse fuse, StrategyRecord strategyRecord) {
        long riskId = strategyRecord.getRiskId();
        long current = System.currentTimeMillis();
        long start = current - TimeUnit.SECONDS.toMillis(fuse.getTime());

        // 先删除过期请求总数数据，再添加新请求总数数据
        String totalKey = String.format(IRiskConstants.STRATEGY_FUSE_TOTAL, strategyRecord.getStrategyId());
        redisComponent.zSetDeleteByScore(totalKey, NumberUtil.ZERO, start);
        redisComponent.zSetAdd(totalKey, riskId, current);
        // 熔断只校验拦截的比例
        if (strategyRecord.getResultType() != ResultTypeEnum.REJECT.getId()) {
            return;
        }
        // 先删除过期拦截总数数据，再添加新拦截总数数据
        String resultKey = String.format(IRiskConstants.STRATEGY_FUSE_RESULT_TYPE, strategyRecord.getStrategyId(), strategyRecord.getResultType());
        redisComponent.zSetDeleteByScore(resultKey, NumberUtil.ZERO, start);
        redisComponent.zSetAdd(resultKey, riskId, current);
        // 获取请求总数和拦截总数
        double totalCount = redisComponent.zSetCard(totalKey);
        double rejectCount = redisComponent.zSetCard(resultKey);
        // 计算熔断比例，判断是否需要熔断
        if (totalCount <= fuse.getCount() || rejectCount / totalCount <= fuse.getRatio()) {
            return;
        }
        // 执行熔断
        StrategyParam param = new StrategyParam();
        param.setId(strategyRecord.getStrategyId());
        param.setStatus(StrategyStatusEnum.FUSE.getCode());
        log.warn("fuseStrategy {} {}", riskId, strategyRecord.getStrategyId());
        strategyService.editStrategy(param);
        // 熔断钉钉通知
        dingTalkComponent.sendFuseMessage(strategyRecord.getStrategyId(), strategyRecord.getStrategyName(), (long) rejectCount, (long) totalCount);
    }
}
